package plumber.coordinator;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;

import plumber.common.WorkItem;
import plumber.common.WorkItem.Status;



public abstract class AbstractPersistentCoordinator extends AbstractCoordinator {

	protected static final String STATUS_COLUMN = "status";
	protected static final String CONTEXT_COLUMN = "context";
	protected static final String VERSION_COLUMN = "version";
	protected static final String DATE_SCHEDULED_COLUMN = "date_scheduled";
	protected static final String DATE_CREATED_COLUMN = "date_created";
	protected static final String RID_COLUMN = "rid";
	protected static final String PK_COLUMN = "id";
	protected static final String STEP_ID_COLUMN = "step_id";
	protected static final String FLOW_NAME_COLUMN = "flow_name";
	protected static final String NAMESPACE_COLUMN = "namespace_name";
	
	
	public String submitWork(String namespace, String context, String flowName) {
		return submitWork(namespace, context, flowName, new Date());
	}
	
	
	public String submitWork(String namespace, String context, String flowName, Date scheduled) {
		WorkItem item = makeNewItem(context, flowName, null, scheduled, UUID
				.randomUUID().toString(), namespace);
		create(item);
		return item.getRequestId();
	}
	
	public abstract void create(WorkItem item);
	
	
	public abstract List<MyWorkItem> itemsMatchingRequestIdAndStatus(String requestId, String status);
			
	
	
	public boolean checkFailed(String requestId) {
		List<MyWorkItem> items = itemsMatchingRequestIdAndStatus(requestId,
				WorkItem.Status.FAILED.toString());
		return (items.size() == 1);
	}


	public String getResult(String requestId) {
		List<MyWorkItem> items = itemsMatchingRequestIdAndStatus(requestId,
				WorkItem.Status.FINAL.toString());
		if(items.size() == 0) {
			throw new RuntimeException("not available yet");
		}
		return items.get(0).getContext();
	}


	@SuppressWarnings("serial")
	protected class MyWorkItem extends WorkItem {
		private int version = 0;

		public int getVersion() {
			return version;
		}

		public void setVersion(int version) {
			this.version = version;
		}
	}


	public abstract boolean updateStatus(String id, String fromStatus, String status,
			String context);


	protected List<WorkItem> makeWorkItems(List<MyWorkItem> query) {
		List<WorkItem> result = new ArrayList<WorkItem>();
		for(MyWorkItem item : query) {
			WorkItem newItem = new WorkItem();
			newItem.setContext(item.getContext());
			newItem.setFlowName(item.getFlowName());
			newItem.setFlowStepId(item.getFlowStepId());
			newItem.setId(item.getId());
			newItem.setRequestId(item.getRequestId());
			newItem.setScheduled(item.getScheduled());
			newItem.setStatus(item.getStatus());
			result.add(newItem);
		}
		return result;
	}


	
	public boolean suspend(String requestId) {
		MyWorkItem candidate = itemMatching(requestId, Status.AVAILABLE.toString());
		return updateStatus(candidate.getId(), 
				Status.AVAILABLE.toString(), 
				Status.SUSPENDED.toString(), 
				candidate.getContext());
	}


	public boolean resume(String requestId) {
		MyWorkItem candidate = itemMatching(requestId, Status.SUSPENDED.toString());
		return updateStatus(candidate.getId(), 
				Status.SUSPENDED.toString(), 
				Status.AVAILABLE.toString(), 
				candidate.getContext());
	}


	private MyWorkItem itemMatching(String requestId, String status) {
		List<MyWorkItem> candidates = this.itemsMatchingRequestIdAndStatus(requestId, 
				status);
		if(candidates.size() == 0) {
			throw new IllegalStateException("no item in status");
		}
		if(candidates.size() > 1) {
			throw new IllegalStateException("more than one item in status");
		}
		return candidates.get(0);
	}


    
    public boolean claimCandidate(String id) {
    	return updateStatus(id, WorkItem.Status.AVAILABLE.toString(), 
    			WorkItem.Status.IN_PROGRESS.toString(), null);
    }

    public boolean checkComplete(String requestId) {
        List<MyWorkItem> items = itemsMatchingRequestIdAndStatus(requestId,
                WorkItem.Status.FINAL.toString());
        return (items.size() == 1);
    }
}
